package ec.atreasure.com.compiler;

import com.google.auto.service.AutoService;

import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

import ec.atreasure.com.annotations.AppRegisterGenerator;
import ec.atreasure.com.annotations.EntryGenerator;
import ec.atreasure.com.annotations.PayEntryGenerator;

@SuppressWarnings("unused")
@AutoService(Processor.class)
public class AtreasureProcessor extends AbstractProcessor {

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        final Set<String> types = new LinkedHashSet<>();
        final Set<Class<? extends Annotation>> supportAnnotations = getSupportedAnnotations();
        for (Class<? extends Annotation> annotation : supportAnnotations) {
            types.add(annotation.getCanonicalName());
        }
        return types;
    }


    private Set<Class<? extends Annotation>> getSupportedAnnotations() {
        final Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
        annotations.add(EntryGenerator.class);
        annotations.add(PayEntryGenerator.class);
        annotations.add(AppRegisterGenerator.class);
        return annotations;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        generateEntryCode(roundEnvironment);
        generatePayEntryCode(roundEnvironment);
        generateAppRegisterCode(roundEnvironment);
        return true;
    }

    private void generateEntryCode(RoundEnvironment roundEnvironment) {
        final EntryVisitor entryVisitor = new EntryVisitor(processingEnv.getFiler());
        scan(roundEnvironment, EntryGenerator.class, entryVisitor);
    }

    private void generatePayEntryCode(RoundEnvironment roundEnvironment) {
        final PayEntryVisitor entryVisitor = new PayEntryVisitor(processingEnv.getFiler());
        scan(roundEnvironment, PayEntryGenerator.class, entryVisitor);
    }

    private void generateAppRegisterCode(RoundEnvironment roundEnvironment) {
        final AppRegisterVisitor entryVisitor = new AppRegisterVisitor(processingEnv.getFiler());
        scan(roundEnvironment, AppRegisterGenerator.class, entryVisitor);
    }

    private void scan(RoundEnvironment roundEnvironment, Class<? extends Annotation> annotation, AnnotationValueVisitor visitor) {
        //遍历所有使用annotation注解的元素
        for (Element typeElement : roundEnvironment.getElementsAnnotatedWith(annotation)) {

            processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "typeElement.toString() is " + typeElement.toString());

            //获取这个注解的完整内容
            final List<? extends AnnotationMirror> annotationMirrors = typeElement.getAnnotationMirrors();

            processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                    "annotationMirrors.toString() is " + annotationMirrors.toString());

            //遍历注解
            for (AnnotationMirror annotationMirror : annotationMirrors) {
                final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror.getElementValues();

                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                        "annotationMirror.toString() is " + annotationMirror.toString());

                //遍历每个注解并去除value
                for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) {

                    //传递value到visitor并生成文件
                    entry.getValue().accept(visitor, null);

                    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                            " entry.getValue() is " + entry.getValue());

                }

                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                        "visitor.toString() is " + visitor.toString());
            }
        }
    }
}
